#ifndef __PROCESSPOOL_HPP__
#define __PROCESSPOOL_HPP__

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <vector>
#include <sstream>
#include <functional>
#include <sys/wait.h>
#include <ctime>
#include "Task.hpp"
using namespace std;
const int gdefault_process_number = 5;
// typedef function<void(int fd)> callback_t;
using callback_t = function<void(int fd)>;

template <typename T>
std::string to_string(T value)
{
    std::ostringstream os;
    os << value;
    return os.str();
}
// describe
class Channel
{
public:
    Channel() {}
    Channel(int fd, const string &name, pid_t id) : _wfd(fd), _name(name), _sub_target(id)
    {
    }
    void DebugPrint()
    {
        cout << "channel name : " << _name << " , write fd : " << _wfd << " , sub process id : " << _sub_target << endl;
    }
    ~Channel() {}

    int FD() { return _wfd; }
    string Name() { return _name; }
    pid_t SubTarget() { return _sub_target; }

    void Close()
    {
        close(_wfd);
    }

    void Wait()
    {
        int status = 0;
        waitpid(_sub_target, &status, 0);
    }

private:
    int _wfd;
    string _name;
    pid_t _sub_target;

    // int _load;
};

class ProcessPool
{
public:
    ProcessPool(int num = gdefault_process_number) : _processnum(num)
    {
        srand((unsigned int)time(nullptr) ^ getpid() ^ 0x777);
    }
    ~ProcessPool() {}

    bool InitProcessPool(callback_t cb)
    {
        for (int i = 0; i < _processnum; i++)
        {
            // 1.create a pipe
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
            {
                perror("pipe error");
                return false;
            }

            // 2.fork a child process
            pid_t id = fork();
            if (id == 0)
            {
                // close write end of pipe
                close(pipefd[1]);
                if(_channels.size() > 0)
                {
                    for(auto &ch : _channels)
                    {
                        ch.Close();
                        cout << getpid() << " child close channel : " << ch.FD() << endl;
                    }
                }
                
                // child do something
                cb(pipefd[0]);

                exit(0);
            }

            close(pipefd[0]);
            string name = "channel-" + to_string(i);
            _channels.emplace_back(pipefd[1], name, id);
        }

        return true;
    }

    void PollingCtrSubProcess()
    {
        int index = 0;
        while (true)
        {
            CtrSubProcess(index);
            index = (index + 1) % _processnum;
        }
    }

    void PollingCtrSubProcess(int count)
    {
        if (count <= 0)
            return;
        int index = 0;
        while (count--)
        {
            CtrSubProcess(index);
            index = (index + 1) % _processnum;
        }
    }

    void RandomCtrSubProcess()
    {
        int index = rand() % _processnum;
        CtrSubProcess(index);
    }

    void LoadCtrSubProcess()
    {
        // TODO
    }

    void WaitSubProcess()
    {
        // for (auto &ch : _channels)
        // {
        //     ch.Close();
        // }
        // for (auto &ch : _channels)
        // {
        //     ch.Wait();
        //     cout << "subprocess : " << ch.SubTarget() << " has exited" << endl;
        // }
        for(auto &ch : _channels)
        {
            ch.Close();
            ch.Wait();
        }
    }

private:
    vector<Channel> _channels; // all channels
    int _processnum;           // process number

    void CtrSubProcess(int &index)
    {
        int who = index;
        int x = rand() % tasks.size();
        cout << "chose channel : " << _channels[who].Name() << " , pid : " << _channels[who].SubTarget() << endl;
        // wake up the child process
        write(_channels[who].FD(), &x, sizeof(x));
        sleep(1);
    }
};
#endif